import axios from 'axios'
import store from '@/store'
import storage from 'store'
import notification from 'ant-design-vue/es/notification'
import { VueAxios } from './axios'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { i18nRender } from '@/locales'

// 请求列表
const requestList = []

// 取消列表
const CancelToken = axios.CancelToken

const sources = {}

// 创建 axios 实例
const request = axios.create({
  // API 请求的默认前缀
  baseURL: process.env.VUE_APP_API_BASE_URL,
  timeout: 6000 // 请求超时时间
})

/**
 * 将一个请求转换成对应的字符串，用于比较多个请求是否相同，这里下定义：
 * 当两个请求的method、完整url、全部参数均相同时，则认为这两个请求为“相同请求”
 * @param {*} config
 */
const genUniqueRequest = config => {
  let argStr = ''
  if (!!config && config.hasOwnProperty('params')) {
    argStr = JSON.stringify(config.params)
  } else if (!!config && config.hasOwnProperty('data')) {
    if (config.data instanceof FormData) {
      // 如果是formData则需要这样处理
      argStr = JSON.stringify(Object.fromEntries(config.data))
    } else {
      argStr = JSON.stringify(config.data)
    }
  }
  return `${config.method}_${config.baseURL}${config.url}_${argStr}`
}

/**
 * 将请求添加至列表
 * @param {*} config
 */
const addRequest = config => {
  const request = genUniqueRequest(config)
  // 如果已经有cancelToken就不要新加了，也不加进requestList
  if (!config.cancelToken) {
    config.cancelToken = new CancelToken(cancel => {
      sources[request] = cancel
    })
    // 1.判断请求是否已存在请求列表，避免重复请求，将当前请求添加进请求列表数组；
    if (requestList.includes(request)) {
      console.log(requestList, request)
      sources[request]('取消重复请求：' + JSON.stringify(request))
    } else {
      requestList.push(request)
      // 2. 如果有全局loading状态，在这里置为true
      // todo
    }
  } else {
    console.log(config, 'cancelToken已存在')
  }
}

/**
 * 从列表中移除请求
 * @param {*} config
 */
const deleteRequest = config => {
  // 1.将当前请求中请求列表中删除
  const request = genUniqueRequest(config)
  requestList.splice(
    requestList.findIndex(item => item === request),
    1
  )
  // 2.当请求列表为空时，如果有全局loading状态，在这里置为false
  if (requestList.length === 0) {
    // todo
  }
}

// 异常拦截处理器
const errorHandler = error => {
  if (error.response) {
    const data = error.response.data
    if (error.response.status === 403) {
      notification.error({
        message: i18nRender('common.error'),
        description: i18nRender(data.message)
      })
    }

    if (error.response.status === 401) {
      cancelAllRequest()
      notification.error({
        message: i18nRender('common.error'),
        description: i18nRender('common.loginTimeOut')
      })
      store.dispatch('Logout').then(() => {
        setTimeout(() => {
          window.location.reload()
        }, 1500)
      })
    }
  } else {
    if (axios.isCancel(error)) {
      error.selfCancel = true
    } else {
      if (error.message.indexOf('timeout') > -1) {
        notification.error({
          message: i18nRender('common.error'),
          description: i18nRender('common.connectionTimeOut')
        })
        deleteRequest(error.config)
      } else {
        if (error.message.indexOf('取消') === -1) {
          // 不包含取消请求
          notification.error({
            message: i18nRender('common.error'),
            description: error.message
          })
          deleteRequest(error.config)
        }
      }
    }
  }
  return Promise.reject(error)
}

/**
 * 取消所有请求
 */
const cancelAllRequest = function () {
  requestList.splice(0, requestList.length)
  for (const prop in sources) {
    if (sources.hasOwnProperty(prop)) {
      sources[prop]('cancelAllRequest ' + JSON.stringify(prop))
    }
  }
}

// request interceptor
request.interceptors.request.use(config => {
  addRequest(config)
  const token = storage.get(ACCESS_TOKEN)
  // 如果 token 存在
  // 让每个请求携带自定义 token 请根据实际情况自行修改
  if (token) {
    config.headers[ACCESS_TOKEN] = token
  }
  return config
}, errorHandler)

// response interceptor
request.interceptors.response.use(response => {
  deleteRequest(response.config)

  const data = response.data
  const { responseType } = response.config

  // 针对流数据做单独处理
  if (responseType === 'blob') {
    // 本应返回流数据却返回了application/json，则说明报错，走错误处理分支
    if (data.type === 'application/json') {
      const reader = new FileReader()
      reader.onload = e => {
        const res = JSON.parse(e.target.result)
        const errorMsg = res.message ? i18nRender(res.message) : i18nRender('common.serverError')
        notification.error({
          message: i18nRender('common.error'),
          description: errorMsg
        })
        return Promise.reject(errorMsg)
      }
      reader.readAsText(data)
    } else {
      return data
    }
  } else {
    if (['unique_error_code', 'ok', 'sso.ok', 'line.side.warehouse.is.in.used', 'mbom.not.found'].includes(data.code)) {
      return data
    } else {
      if (data.code === 401) {
        cancelAllRequest()
        notification.error({
          message: i18nRender('common.error'),
          description: i18nRender('common.loginTimeOut')
        })
        store.dispatch('Logout').then(() => {
          setTimeout(() => {
            window.location.reload()
          }, 1500)
        })
      } else {
        // 如果是主动校验的返回，则需要将message抓取并展示到UI对应位置
        const { headers, responseType } = response.config
        if ((!!headers && headers.validate) || responseType === 'blob') {
          return data
        }
        const errorMsg = data.message ? i18nRender(data.message) : i18nRender('common.serverError')
        notification.error({
          message: i18nRender('common.error'),
          description: errorMsg
        })
        return Promise.reject(errorMsg)
      }
    }
  }
}, errorHandler)

const installer = {
  vm: {},
  install (Vue) {
    Vue.use(VueAxios, request)
  }
}

export default request

export {
  installer as VueAxios,
  request as axios,
  cancelAllRequest
}
